/*
 * SPDX-FileCopyrightText: 2023-2023 liulinsong <liulinsong@kylinos.cn>
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 */

#include "statusmanagerwatcher.h"

namespace fcitx {

StatusManagerWatcher::StatusManagerWatcher(std::unique_ptr<Strategy> strategy,
                                           Instance *instance)
    : strategy_(std::move(strategy)), instance_(instance),
      bus_(dbus()->call<IDBusModule::bus>()), watcher_(*bus_) {
    updateSubjectDefaultAvailable();

    watch();

    connectSignal();
}

StatusManagerWatcher::~StatusManagerWatcher() = default;

void StatusManagerWatcher::updateSubjectDefaultAvailable() {
    updateSubjectAvailable(strategy_->isSubjectDefaultAvailable());
}

void StatusManagerWatcher::watch() {
    entry_ = watcher_.watchService(
        "com.kylin.statusmanager.interface",
        [this](const std::string &, const std::string &,
               const std::string &newOwner) {
            FCITX_INFO() << "StatusManagerWatcher's strategy: "
                         << strategy_->getName() << " new owner: " << newOwner;
            if (!newOwner.empty()) {
                setSubjectAvailable(isSubjectAvailableDBus());
            } else {
                updateSubjectDefaultAvailable();
            }
        });
}

void StatusManagerWatcher::connectSignal() {
    slot_ =
        bus_->addMatch(dbus::MatchRule("com.kylin.statusmanager.interface", "/",
                                       "com.kylin.statusmanager.interface",
                                       strategy_->getDbusSignalName()),
                       [this](dbus::Message &message) {
                           const bool isSubjectAvailable =
                               isSubjectAvailableFromSignalMessage(message);
                           setSubjectAvailable(isSubjectAvailable);

                           return true;
                       });
}

bool StatusManagerWatcher::isSubjectAvailableFromSignalMessage(
    dbus::Message &message) const {
    bool isSubjectAvailable = strategy_->isSubjectDefaultAvailable();
    message >> isSubjectAvailable;

    return isSubjectAvailable;
}

void StatusManagerWatcher::setSubjectAvailable(bool available) {
    if (isSubjectAvailable_ == available) {
        return;
    }

    updateSubjectAvailable(available);
}

void StatusManagerWatcher::updateSubjectAvailable(bool available) {
    isSubjectAvailable_ = available;

    strategy_->nofityDeviceAvailabilityChanged();
}

bool StatusManagerWatcher::isSubjectAvailableDBus() {
    bool available = strategy_->isSubjectDefaultAvailable();
    callDBusMethod(strategy_->getDbusMethodName(), [this, &available](
                                                       dbus::Message &message) {
        available = strategy_->isSubjectAvailableFromMethodMessage(message);
    });

    return available;
}

void StatusManagerWatcher::callDBusMethod(
    const std::string &methodName, const MethodCallback &methodCallback) {
    if (!methodCallback) {
        return;
    }

    auto msg = bus_->createMethodCall("com.kylin.statusmanager.interface", "/",
                                      "com.kylin.statusmanager.interface",
                                      methodName.c_str());
    methodCallback(msg);
}

} // namespace fcitx
